/* 
 * This file contains classes for the project management
 */

// ======================== PROJECT ========================
// 
// Class that representation the diagram.
function Project(name) {
    // name of the diagram
    this.name = name;
    // event source
    this.eventDispatcher = new EventDispatcher();
    // model that holds model elements used in this project
    this.model = new Model();
    this.model.eventDispatcher.registerListener(this);
    // list of diagrams in this project
    this.diagrams = new Array();
    this.activeDiagrams = new Array();
    this.currentDiagram = null;
    this.diagramCount = 0;

    this.currentEditor = null;

    this.treeViewHtml = projectExplorerTreeDiv;
    this.editorHtmlDiv = editorDiv;
    this.diagramCanvasHtml = canvas;

    this.treeView = new dTree(this.treeViewHtml.name);

    defaultUidGenerator = new UIDGenerator(100000);

    this.resizeDiagramCanvas();
    // FIXME uncomment this if loadProject is done, with a new (empty) project
    // there should be a diagram there to use. If loading a project, it should 
    // only contain diagrams from the xml
//    this.createNewDiagram();
    this.buildTreeView();
}

//
// ######################## BASIC PROPERTIES MANAGEMENT ########################
//

Project.prototype.setName = function(name) {
    this.name = name;
    this.buildTreeView();
};
Project.prototype.setEditor = function(editor) {
    if (editor === null) {
        // FIXME: create a project editor
//        this.currentEditor = this.projectEditor;
        if (this.currentEditor) {
            this.currentEditor.deleteData();
        }
        this.currentEditor = null;
        return;
    }

    this.currentEditor = editor;
    this.currentEditor.fillHtml(this.editorHtmlDiv);
};

//
// ######################## MODEL MANAGEMENT ########################
//

Project.prototype.setModel = function(model) {
    this.model.eventDispatcher.removeListener(this);
    this.model = model;
    this.model.eventDispatcher.registerListener(this);
    this.buildTreeView();
};
Project.prototype.addModelElement = function(modelElement) {
    if (modelElement === undefined || modelElement === null) {
        return;
    }

    this.model.addElement(modelElement);
    this.buildTreeView();
};
Project.prototype.removeModelElement = function(modelElement) {
    if (modelElement === undefined || modelElement === null) {
        return;
    }

    this.model.removeElement(modelElement);
    this.buildTreeView();
};

//
// ######################## DIAGRAM MANAGEMENT ########################
//

Project.prototype.addDiagram = function(diagram) {
    if (diagram === undefined || diagram === null) {
        return;
    }

    this.diagrams.push(diagram);
    diagram.evCanvasDimensionChanged(new Dimension(this.diagramCanvasHtml.width, this.diagramCanvasHtml.height));

    // subscribe to events from the diagram
    diagram.eventDispatcher.registerListener(this);

    if (this.currentDiagram === null) {
        this.openDiagram(diagram);
    } else {
        diagram.setAsActive(true);
        this.activeDiagrams.push(diagram);
        this.drawDiagramHeader();
    }

    this.buildTreeView();
};
Project.prototype.removeDiagram = function(diagram) {
    if (diagram === undefined || diagram === null) {
        return;
    }

    // unsubscribe to events from the diagram
    diagram.eventDispatcher.removeListener(this);

    this.closeDiagram(diagram);
    this.diagrams.remove(diagram);
    diagram.destroy();
    this.buildTreeView();
};
Project.prototype.createNewDiagram = function() {
    var alreadyExistsWithThatName;
    var nextName;
    do {
        alreadyExistsWithThatName = false;
        this.diagramCount++;
        nextName = "Diagram" + this.diagramCount;
//        for (var i = 0; i < this.diagrams.length; i++) {
//            if (this.diagrams[i].name === nextName) {
//                alreadyExistsWithThatName = true;
//            }
//        }
    } while (this.getDiagramByName(nextName) !== null);

    var newDiagram = new Diagram(nextName);

    // subscribe to events from the diagram
    newDiagram.eventDispatcher.registerListener(this);

    this.diagrams.push(newDiagram);
    this.openDiagram(newDiagram);
    this.buildTreeView();
};
Project.prototype.getDiagramByName = function(diagramName) {
    for (var i = 0; i < this.diagrams.length; i++) {
        if (this.diagrams[i].name === diagramName) {
            return this.diagrams[i];
        }
    }
    return null;
};
Project.prototype.getDiagramById = function(diagramId) {
    for (var i = 0; i < this.diagrams.length; i++) {
        if (this.diagrams[i].uid === diagramId) {
            return this.diagrams[i];
        }
    }
    return null;
};
Project.prototype.openDiagram = function(diagram) {
    if (!diagram.isActive) {
        diagram.setAsActive(true);
        this.activeDiagrams.push(diagram);
    }
    this.switchToDiagram(diagram);
};
Project.prototype.closeDiagram = function(diagram) {
    if (diagram.isActive) {
        diagram.setAsActive(false);

        var idx = this.activeDiagrams.remove(diagram);

        if (this.activeDiagrams.length > 0) {
            this.switchToDiagram(this.activeDiagrams[Clamp(idx - 1, 0, this.diagrams.length - 1)]);
        } else {
            this.switchToDiagram(null);
        }
    }
};
Project.prototype.switchToDiagram = function(diagram) {
    if (this.currentDiagram !== null) {
        this.currentDiagram.evDiagramLeave();
    }

    this.setEditor(null);
    this.currentDiagram = diagram;

    if (diagram !== null) {
        this.currentDiagram.evDiagramEnter();
        if (this.currentDiagram.selectedElement !== null) {
            this.setEditor(this.currentDiagram.selectedElement.createEditor());
        } else {
            if (this.currentDiagram) {
                this.setEditor(this.currentDiagram.createEditor());
            }
        }
    }

    this.drawDiagramHeader();
};

//
// ######################## PROJECT HANDLING FUNCTIONS ########################
//

Project.prototype.newProject = function() {
    this.name = "Project1";
    // event source
    this.eventDispatcher = new EventDispatcher();
    // model that holds model elements used in this project
    this.model.eventDispatcher.removeListener(this);
    this.model = new Model();
    this.model.eventDispatcher.registerListener(this);
    // list of diagrams in this project
    this.diagrams = new Array();
    this.activeDiagrams = new Array();
    this.currentDiagram = null;
    this.diagramCount = 0;

    this.treeView = new dTree(this.treeViewHtml.name);

    defaultUidGenerator = new UIDGenerator(100000);

    this.buildTreeView();
};
Project.prototype.saveProject = function() {

    var xmlWriter = new XmlWriter();
    xmlWriter.beginDocument();

    xmlWriter.beginNode("project");
    xmlWriter.addAttribute("name", this.name);
    xmlWriter.addAttribute("version", "0.1");
    xmlWriter.beginNodeChildren();

    xmlWriter.beginNode("project-settings");
    xmlWriter.beginNodeChildren();
    xmlWriter.beginNode("id-generator");
    xmlWriter.addAttribute("nextValue", defaultUidGenerator.nextVal);
    xmlWriter.endNode();
    xmlWriter.endNodeChildren();
    xmlWriter.endNode();

    this.model.toXml(xmlWriter);

    xmlWriter.addCommentChild("Diagrams go here");

    for (var i = 0; i < this.diagrams.length; i++) {
        this.diagrams[i].toXml(xmlWriter);
    }

    xmlWriter.endNodeChildren();
    xmlWriter.endNode();

    // TODO save xmlWriter.toString to file

    ShowOutput(xmlWriter.toString());
};
Project.prototype.loadProjectFromXML = function(sourceXML) {

    var xmlDoc = sourceXML;

    // Project
    var xmlProject = xmlDoc.getElementsByTagName("project")[0];
    this.newProject();
    this.setName(xmlProject.getAttribute("name"));

    // Model
    var xmlModel = xmlProject.getElementsByTagName("model")[0];
    var model = new Model();

    // Interfaces without operations
    var xmlModelInterfaces = xmlModel.getElementsByTagName("interface");
    for (var i = 0; xmlModelInterfaces[i]; i++)
    {
        var interfacei = new Class(
                xmlModelInterfaces[i].getAttribute("name"), true);
        interfacei.setUid(
                xmlModelInterfaces[i].getAttribute("id"));
        interfacei.setVisibility(convertVisibilityXmlToModel(
                xmlModelInterfaces[i].getAttribute("visibility")));

        model.addElement(interfacei);
    }

    // Classes without attributes or operations
    var xmlModelClasses = xmlModel.getElementsByTagName("class");
    for (var i = 0; xmlModelClasses[i]; i++)
    {
        var classi = new Class(xmlModelClasses[i].getAttribute("name"));
        classi.setUid(xmlModelClasses[i].getAttribute("id"));
        classi.setVisibility(convertVisibilityXmlToModel(
                xmlModelClasses[i].getAttribute("visibility")));
        classi.setIsAbstract(
                xmlModelClasses[i].getAttribute("isAbstract") === "true");

        model.addElement(classi);
    }

    // Interface Operations
    for (var i = 0; xmlModelInterfaces[i]; i++)
    {
        var interfacei = model.getElementById(
                xmlModelInterfaces[i].getAttribute("id"));

        // Operations
        var iOperations =
                xmlModelInterfaces[i].getElementsByTagName("operation");
        for (var j = 0; iOperations[j]; j++)
        {
            var opj = new Operation(iOperations[j].getAttribute("name"));
            opj.setUid(iOperations[j].getAttribute("id"));
            opj.setVisibility(convertVisibilityXmlToModel(
                    iOperations[j].getAttribute("visibility")));
            opj.setIsClassifierScope(iOperations[j].getAttribute("isClassifier"));
            opj.setIsAbstract(
                    iOperations[j].getAttribute("isAbstract") === "true");

            var typeref = iOperations[j].getAttribute("returnTypeRef");
            if (typeref)
                opj.setReturnType(getTypeFromId(typeref, model));

            // Parameters
            var iOpParams = iOperations[j].getElementsByTagName("parameter");
            for (var k = 0; iOpParams[k]; k++)
            {
                var paramk = new Parameter(
                        iOpParams[k].getAttribute("name"));
                paramk.setUid(
                        iOpParams[k].getAttribute("id"));

                var typeRef = iOpParams[k].getAttribute("typeRef");
                if (typeRef)
                    paramk.setType(getTypeFromId(typeRef, model));

                var defValue = iOpParams[k].getAttribute("defaultValue");
                if (defValue)
                    paramk.setDefaultValue(defValue);

                opj.addParameter(paramk);
            }

            interfacei.addOperation(opj);
        }
    }

    // Class attributes and operations
    xmlModelClasses = xmlModel.getElementsByTagName("class");
    for (var i = 0; xmlModelClasses[i]; i++)
    {
        var classi = model.getElementById(
                xmlModelClasses[i].getAttribute("id"));

        // Attributes
        var iAttributes =
                xmlModelClasses[i].getElementsByTagName("attribute");
        for (var j = 0; iAttributes[j]; j++)
        {
            var atrj = new Attribute(iAttributes[j].getAttribute("name"));

            atrj.setUid(iAttributes[j].getAttribute("id"));
            atrj.setVisibility(convertVisibilityXmlToModel(
                    iAttributes[j].getAttribute("visibility")));
            atrj.setIsClassifierScope(
                    iAttributes[j].getAttribute("isClassifier") === "true");

            var typeRef = iAttributes[j].getAttribute("typeRef");
            if (typeRef)
                atrj.setType(getTypeFromId(typeRef, model));

            var defValue = iAttributes[j].getAttribute("defaultValue");
            if (defValue)
                atrj.setDefaultValue(defValue);

            var multiplicity = iAttributes[j].getAttribute("multiplicity");
            if (multiplicity)
                atrj.setMultiplicity(multiplicity);

            classi.addAttribute(atrj);
        }

        // Operatinos
        var iOperations =
                xmlModelClasses[i].getElementsByTagName("operation");
        for (var j = 0; iOperations[j]; j++)
        {
            var opj = new Operation(iOperations[j].getAttribute("name"));
            opj.setUid(iOperations[j].getAttribute("id"));
            opj.setVisibility(convertVisibilityXmlToModel(
                    iOperations[j].getAttribute("visibility")));
            opj.setIsClassifierScope(
                    iOperations[j].getAttribute("isClassifier") === "true");
            opj.setIsAbstract(
                    iOperations[j].getAttribute("isAbstract") === "true");

            var typeref = iOperations[j].getAttribute("returnTypeRef");
            if (typeref)
                opj.setReturnType(getTypeFromId(typeref, model));

            // Parameters
            var opjParams = iOperations[j].getElementsByTagName("parameter");
            for (var k = 0; opjParams[k]; k++)
            {
                var paramk = new Parameter(
                        opjParams[k].getAttribute("name"));
                paramk.setUid(
                        opjParams[k].getAttribute("id"));

                var typeRef = opjParams[k].getAttribute("typeRef");
                if (typeRef)
                    paramk.setType(getTypeFromId(typeRef, model));

                var defValue = opjParams[k].getAttribute("defaultValue");
                if (defValue)
                    paramk.setDefaultValue(defValue);

                opj.addParameter(paramk);
            }

            classi.addOperation(opj);
        }
    }

    // Aggregations, Associations and Compositions
    var xmlModelAggregations = xmlModel.getElementsByTagName("association");
    for (var i = 0; xmlModelAggregations[i]; i++)
    {
        var associ = new Association(
                xmlModelAggregations[i].getAttribute("name"),
                false, false, false);
        associ.setUid(
                xmlModelAggregations[i].getAttribute("id"));
        associ.setIsAggregation(
                xmlModelAggregations[i].getAttribute("isAggregation")
                === "true");
        associ.setIsAssociation(
                xmlModelAggregations[i].getAttribute("isAssociation")
                === "true");
        associ.setIsComposition(
                xmlModelAggregations[i].getAttribute("isComposition")
                === "true");

        var associEnds = xmlModelAggregations[i]
                .getElementsByTagName("association-end");
        for (var j = 0; associEnds[j]; j++)
        {
            if (associEnds[j].getAttribute("end") === "source")
            {
                associ.sourceEndInfo.setName(associEnds[j].getAttribute("name"));
                associ.sourceEndInfo.setVisibility(convertVisibilityXmlToModel(associEnds[j].getAttribute("visibility")));
                associ.sourceEndInfo.setMultiplicity(associEnds[j].getAttribute("multiplicity"));
                associ.sourceEndInfo.setIsNavigable(associEnds[j].getAttribute("isNavigable") === "true");

                associ.setSourceEnd(getTypeFromId(associEnds[j].getAttribute("typeRef"), model));
            }
            else // target
            {
                associ.targetEndInfo.setName(associEnds[j].getAttribute("name"));
                associ.targetEndInfo.setVisibility(convertVisibilityXmlToModel(associEnds[j].getAttribute("visibility")));
                associ.targetEndInfo.setMultiplicity(associEnds[j].getAttribute("multiplicity"));
                associ.targetEndInfo.setIsNavigable(associEnds[j].getAttribute("isNavigable") === "true");

                associ.setTargetEnd(getTypeFromId(associEnds[j].getAttribute("typeRef"), model));
            }
//            try {
//                //var multiplicity = associEnds[j].getAttribute("multiplicity");
//                //if (multiplicity) associ.setMultiplicity(multiplicity);
//                associ.setMultiplicity(associEnds[j].getAttribute("multiplicity"));
//            } catch (e) {
//            }
        }

        model.addElement(associ);
    }

    // Generalizations and Realizations
    var xmlModelInheritances = xmlModel.getElementsByTagName("inheritance");
    for (var i = 0; xmlModelInheritances[i]; i++)
    {
        var inheriti = new Inheritance(
                xmlModelInheritances[i].getAttribute("name"),
                xmlModelInheritances[i].getAttribute("isGeneralization")
                === "true",
                xmlModelInheritances[i].getAttribute("isRealization")
                === "true");
        inheriti.setUid(
                xmlModelInheritances[i].getAttribute("id"));
        inheriti.setSourceEnd(getTypeFromId(
                xmlModelInheritances[i].getAttribute("sourceRef"),
                model));
        inheriti.setTargetEnd(getTypeFromId(
                xmlModelInheritances[i].getAttribute("targetRef"),
                model));

        model.addElement(inheriti);
    }

    // Dependencies
    var xmlModelDependencies = xmlModel.getElementsByTagName("dependency");
    for (var i = 0; xmlModelDependencies[i]; i++)
    {
        var depi = new Dependency(
                xmlModelDependencies[i].getAttribute("name"));
        depi.setUid(
                xmlModelDependencies[i].getAttribute("id"));
        depi.setSourceEnd(getTypeFromId(
                xmlModelDependencies[i].getAttribute("sourceRef"),
                model));
        depi.setTargetEnd(getTypeFromId(
                xmlModelDependencies[i].getAttribute("targetRef"),
                model));

        model.addElement(depi);
    }

    // Stereotypes
    var xmlModelStereotypes = xmlModel.getElementsByTagName("stereotype");
    for (var i = 0; xmlModelStereotypes[i]; i++)
    {
        var stereotypei = new Stereotype(
                xmlModelStereotypes[i].getAttribute("name"));
        stereotypei.setUid(xmlModelStereotypes[i].getAttribute("id"));

        model.getElementByIdFromAll(
                xmlModelStereotypes[i].getAttribute("attachRef"))
                .addStereotype(stereotypei);


        model.addElement(stereotypei);
    }

    project.setModel(model);

    // Diagrams
    var xmlDiagrams = xmlProject.getElementsByTagName("diagram");
    for (var i = 0; xmlDiagrams[i]; i++)
    {
        var diagrami = new Diagram(
                xmlDiagrams[i].getAttribute("name"));

        var dim = xmlDiagrams[i].getAttribute("dimension");
        var dims = dim.split(",");
        var diagramDimension = new Dimension(dims[0], dims[1]);
        diagrami.setViewDimension(diagramDimension);

        diagrami.setViewPosition(
                StringToPosition(
                xmlDiagrams[i].getAttribute("viewPosition")));

        // Diagram Elements
        var xmlDiagramElements =
                xmlDiagrams[i].getElementsByTagName("class-element");
        for (j = 0; xmlDiagramElements[j]; j++)
        {
            var clgraph = new ClassGraphics(
                    StringToPosition(
                    xmlDiagramElements[j].getAttribute("position")),
                    getTypeFromId(
                    xmlDiagramElements[j].getAttribute("modelRef"),
                    model));
            clgraph.setUid(Number(
                    xmlDiagramElements[j].getAttribute("id")));

            diagrami.addElement(clgraph);
        }

        // Diagram Relationships
        var xmlDiagramRelationships =
                xmlDiagrams[i].getElementsByTagName("relationship-element");
        for (j = 0; xmlDiagramRelationships[j]; j++)
        {
            var relationship = new RelationshipGraphics(
                    getTypeFromId(
                    xmlDiagramRelationships[j].getAttribute("modelRef"),
                    model),
                    diagrami.getElementByDiagramId(
                    xmlDiagramRelationships[j]
                    .getAttribute("sourceElement")),
                    diagrami.getElementByDiagramId(
                    xmlDiagramRelationships[j]
                    .getAttribute("targetElement")));

            relationship.setUid(Number(
                    xmlDiagramRelationships[j].getAttribute("id")));

            relationship.setSourceEndPosition(
                    StringToPosition(xmlDiagramRelationships[j]
                    .getAttribute("sourcePosition")));

            relationship.setTargetEndPosition(
                    StringToPosition(xmlDiagramRelationships[j]
                    .getAttribute("targetPosition")));

            diagrami.addElement(relationship);
        }

        var xmlGeneratorTag = xmlProject.getElementsByTagName("project-settings")[0]
                .getElementsByTagName("id-generator")[0];
        var generatorNextVal = Number(xmlGeneratorTag.getAttribute("nextValue"));

        defaultUidGenerator = new UIDGenerator(generatorNextVal);

        project.addDiagram(diagrami);
        project.switchToDiagram(diagrami);

    }

};

//
// ######################## DRAWING FUNCTIONS ########################
//

// build up the treeview from the model and diagrams currently availlable
Project.prototype.buildTreeView = function() {

    var idx = 0;
    var idxProject;
    var idxModel;
    var idxDiagrams;

    var idxInterfaces;
    var idxClasses;
    var idxRelationships;

    this.treeView = new dTree(this.treeViewHtml.name);

    idxProject = idx;
    this.treeView.add(idx++, -1, this.name, "");
    idxModel = idx;
    this.treeView.add(idx++, idxProject, "Model", "", "Model", "", "media/dtree_img/folder.gif", "media/dtree_img/folderopen.gif");
    idxDiagrams = idx;
    this.treeView.add(idx++, idxProject, "Diagrams", "", "Diagrams", "", "media/dtree_img/folder.gif", "media/dtree_img/folderopen.gif");

    idxInterfaces = idx;
    this.treeView.add(idx++, idxModel, "Interfaces", "", "Interfaces", "", "media/dtree_img/folder.gif", "media/dtree_img/folderopen.gif");
    idxClasses = idx;
    this.treeView.add(idx++, idxModel, "Classes", "", "Classes", "", "media/dtree_img/folder.gif", "media/dtree_img/folderopen.gif");
    idxRelationships = idx;
    this.treeView.add(idx++, idxModel, "Relationships", "", "Relationships", "", "media/dtree_img/folder.gif", "media/dtree_img/folderopen.gif");

    for (var i = 0; i < this.model.modelElements.length; i++) {
        var e = this.model.modelElements[i];
        if (e instanceof Class) {

            var parentNodeIdx, icon;
            if (e.isInterface) {
                parentNodeIdx = idxInterfaces;
                icon = "media/icons/dtree_icon_interface.png";
            } else {
                parentNodeIdx = idxClasses;
                icon = "media/icons/dtree_icon_class.png";
            }

            this.treeView.add(idx++, parentNodeIdx, e.name,
                    "javascript: project.evAddModelElementToCurrentDiagram(\'" + e.uid + "\');",
                    "Add " + e.name + " to diagram", "", icon, icon);

        } else if (e instanceof Relationship) {

            var icon;
            if (e instanceof Association) {
                if (e.isAssociation)
                    icon = "media/icons/dtree_icon_association.png";
                if (e.isAggregation)
                    icon = "media/icons/dtree_icon_aggregation.png";
                if (e.isComposition)
                    icon = "media/icons/dtree_icon_composition.png";
            } else if (e instanceof Inheritance) {
                if (e.isGeneralization)
                    icon = "media/icons/dtree_icon_generalization.png";
                if (e.isRealization)
                    icon = "media/icons/dtree_icon_realization.png";
            } else if (e instanceof Dependency) {
                icon = "media/icons/dtree_icon_dependency.png";
            }

            this.treeView.add(idx++, idxRelationships,
                    e.name,
                    "javascript: project.evAddModelElementToCurrentDiagram(\'" + e.uid + "\');",
                    "Add " + e.name + " to diagram", "", icon, icon);

        }
    }

    for (var i = 0; i < this.diagrams.length; i++) {
        var d = this.diagrams[i];
        var icon = "media/icons/dtree_icon_diagram.png";
        this.treeView.add(idx++, idxDiagrams, d.name, "javascript: project.evDiagramOpen(" + d.uid + ");", "Open " + d.name, "", icon, icon);
    }

    projectExplorerTreeDiv.innerHTML = "| <a href=\"javascript: project.treeView.openAll();\">open all</a>"
            + " | <a href=\"javascript: project.treeView.closeAll();\">close all</a> |" + this.treeView;

    this.treeView.openAll();

};
Project.prototype.drawDiagramHeader = function() {
    diagramHeaderDiv.innerHTML = "";
    for (var i = 0; i < this.activeDiagrams.length; i++) {
        var d = this.activeDiagrams[i];

        diagramHeaderDiv.innerHTML += "<div " +
                "class=" + ((d === this.currentDiagram) ? "diagram_tab_selected" : "diagram_tab") + " " +
                ((d !== this.currentDiagram) ? "onclick=\"project.evDiagramSwitch(" + d.uid + ");\" " : "") +
                "title=\"" + ((d !== this.currentDiagram) ? ("Switch to diagram \'" + d.name + "\'") : ("Current diagram")) + "\" " +
                ">" + d.name +
                ((d === this.currentDiagram) ?
                        "&nbsp;<img src=\"media/icons/icon_delete_16px.png\" class=\"diagram_tab_close_icon\"" +
                        "onclick=\"project.evDiagramClose(" + d.uid + ");\" " +
                        "title=\"Close diagram \'" + d.name + "\'\"" + "/>" : "") +
                "</div>";
//        diagramHeaderDiv.innerHTML += "<input " +
//                "type=\"button\" " +
//                "class=\"" + ((d === this.currentDiagram) ? "diagram_tab_selected" : "diagram_tab") + "\" " +
//                "onclick=\"project.evDiagramSwitch('" + d.name + "');\" " +
//                "value=\"" + d.name + "\" " +
//                "title=\"" + ((d !== this.currentDiagram) ? ("Switch to diagram \'" + d.name + "\'") : ("Current diagram")) + "\" " +
//                "/>";
    }
};
Project.prototype.resizeDiagramCanvas = function() {
    var canvasDiv = this.diagramCanvasHtml.parentNode;

    var w = this.diagramCanvasHtml.width = canvasDiv.scrollWidth;
    var h = this.diagramCanvasHtml.height = canvasDiv.scrollHeight;

    var dim = new Dimension(w, h);

    for (var i = 0; i < this.diagrams.length; i++) {
        this.diagrams[i].evCanvasDimensionChanged(dim);
    }
};
//
// ######################## EVENT HANDLERS ########################
//

// Event callback (dispatchers)
Project.prototype.handleEvent = function(event) {

    if (event.type === "selectionChanged") {
        if (event.selectedElement !== null) {
            this.setEditor(event.selectedElement.createEditor());
        } else {
            if (this.currentDiagram) {
                this.setEditor(this.currentDiagram.createEditor());
            }
        }
    } else if (event.type === "propertyChanged") {
        if (event.source instanceof Diagram) {
            this.drawDiagramHeader();
            this.buildTreeView();
        }
    } else if (event.type === "modelChanged") {
        this.drawDiagramHeader();
        this.buildTreeView();
    }

};

Project.prototype.evDiagramOpen = function(diagId) {
    var diag = this.getDiagramById(diagId);
    if (diag !== null) {
        this.openDiagram(diag);
    }
};
Project.prototype.evDiagramClose = function(diagId) {
    var diag = this.getDiagramById(diagId);
    if (diag !== null) {
        this.closeDiagram(diag);
    }
};
Project.prototype.evDiagramSwitch = function(diagId) {
    var diag = this.getDiagramById(diagId);
    if (diag !== null) {
        this.switchToDiagram(diag);
    }
};
Project.prototype.evDiagramDelete = function() {
    
    if (this.currentDiagram) {

        var c = confirm("This will delete the diagram PERMANENTLY.\n"+
                        "(Model elements displayed on it will be retained)\n"+
                        "\n"+
                        "Are you sure you want to continue?");

        if(c!==true) return;

        this.removeDiagram(this.currentDiagram);
    }
};
Project.prototype.evCreateNewClass = function() {
    if (this.currentDiagram !== null) {
        var newClass = new Class("ClassName", false);

        this.currentDiagram.setLeftMouseClickCallback(new CreateClassCallback(newClass, this.currentDiagram, this.model));
        this.currentDiagram.setLeftMouseDragCallback(null);
    } else {
        alert("You need a diagram before placing elements.");
    }
};
Project.prototype.evCreateNewInterface = function() {
    if (this.currentDiagram !== null) {
        var newClass = new Class("InterfaceName", true);
        var newStereo = new Stereotype("interface");
        newClass.addStereotype(newStereo);

        this.currentDiagram.setLeftMouseClickCallback(new CreateClassCallback(newClass, this.currentDiagram, this.model));
        this.currentDiagram.setLeftMouseDragCallback(null);
    } else {
        alert("You need a diagram before placing elements.");
    }
};
Project.prototype.evCreateNewAttribute = function(parentEditor) {
    if (this.currentDiagram !== null && this.currentDiagram.selectedElement !== null) {

        parentEditor = parentEditor || this.currentDiagram.selectedElement.createEditor();

        var obj = this.currentDiagram.selectedElement.model;
        if (!obj.isInterface) {
            var newAttr = new Attribute("unnamedField");
            obj.addAttribute(newAttr);
            this.setEditor(new AttributeEditor(newAttr, parentEditor));

            this.currentDiagram.setLeftMouseClickCallback(null);
            this.currentDiagram.setLeftMouseDragCallback(null);
        } else {
            alert("You cannot add an attribute to an interface.");
        }
    } else {
        alert("You need to select a class to add an attribute.");
    }
};
Project.prototype.evCreateNewOperation = function(parentEditor) {
    if (this.currentDiagram !== null && this.currentDiagram.selectedElement !== null) {

        parentEditor = parentEditor || this.currentDiagram.selectedElement.createEditor();

        var obj = this.currentDiagram.selectedElement.model;
        if (this.currentDiagram.selectedElement instanceof ClassGraphics) {
            var newOp = new Operation("unnamedOperation");
            obj.addOperation(newOp);
            this.setEditor(new OperationEditor(newOp, parentEditor));

            this.currentDiagram.setLeftMouseClickCallback(null);
            this.currentDiagram.setLeftMouseDragCallback(null);
        } else {
            alert("You need to select a class/interface to add an operation.");
        }
    } else {
        alert("You need to select a class/interface to add an operation.");
    }
};
Project.prototype.evCreateNewParameter = function(parentEditor) {
    if (this.currentEditor !== null && this.currentEditor.modelElement !== null) {

        parentEditor = parentEditor || this.currentDiagram.selectedElement.createEditor();

        var op = this.currentEditor.modelElement;
        var newParam = new Parameter("unnamedParam");
        op.addParameter(newParam);
        this.setEditor(new ParameterEditor(newParam, parentEditor));

        this.currentDiagram.setLeftMouseClickCallback(null);
        this.currentDiagram.setLeftMouseDragCallback(null);

    } else {
//        alert("You need to select a class to add an attribute.");
    }
};
Project.prototype.evCreateNewStereotype = function(parentEditor) {
    if (this.currentEditor !== null && this.currentEditor.modelElement !== null) {

        parentEditor = parentEditor || this.currentDiagram.selectedElement.createEditor();

        var element = this.currentEditor.modelElement;
        var newStereotype = new Stereotype("");
        element.addStereotype(newStereotype);
        this.addModelElement(newStereotype);
        this.setEditor(new StereotypeEditor(newStereotype, parentEditor));

        this.currentDiagram.setLeftMouseClickCallback(null);
        this.currentDiagram.setLeftMouseDragCallback(null);

    } else {
//        alert("You need to select a class to add an attribute.");
    }
};
Project.prototype.evCreateNewAssociation = function() {
    if (this.currentDiagram !== null) {
        var newRelationship = new Association("Unnamed association", true, false, false);
        newRelationship.sourceEndInfo.setIsNavigable(false);

        this.currentDiagram.setLeftMouseClickCallback(null);
        this.currentDiagram.setLeftMouseDragCallback(new CreateRelationshipCallback(newRelationship, this.currentDiagram, this.model));
    } else {
        alert("You need a diagram opened before placing elements.");
    }
};
Project.prototype.evCreateNewAggregation = function() {
    if (this.currentDiagram !== null) {
        var newRelationship = new Association("Unnamed aggregation", false, true, false);
        newRelationship.sourceEndInfo.setIsNavigable(false);

        this.currentDiagram.setLeftMouseClickCallback(null);
        this.currentDiagram.setLeftMouseDragCallback(new CreateRelationshipCallback(newRelationship, this.currentDiagram, this.model));
    } else {
        alert("You need a diagram opened before placing elements.");
    }
};
Project.prototype.evCreateNewComposition = function() {
    if (this.currentDiagram !== null) {
        var newRelationship = new Association("Unnamed composition", false, false, true);
        newRelationship.sourceEndInfo.setIsNavigable(false);

        this.currentDiagram.setLeftMouseClickCallback(null);
        this.currentDiagram.setLeftMouseDragCallback(new CreateRelationshipCallback(newRelationship, this.currentDiagram, this.model));
    } else {
        alert("You need a diagram opened before placing elements.");
    }
};
Project.prototype.evCreateNewDependency = function() {
    if (this.currentDiagram !== null) {
        var newRelationship = new Dependency("Unnamed dependency");

        this.currentDiagram.setLeftMouseClickCallback(null);
        this.currentDiagram.setLeftMouseDragCallback(new CreateRelationshipCallback(newRelationship, this.currentDiagram, this.model));
    } else {
        alert("You need a diagram opened before placing elements.");
    }
};
Project.prototype.evCreateNewGeneralization = function() {
    if (this.currentDiagram !== null) {
        var newRelationship = new Inheritance("Unnamed generalization", true, false);

        this.currentDiagram.setLeftMouseClickCallback(null);
        this.currentDiagram.setLeftMouseDragCallback(new CreateRelationshipCallback(newRelationship, this.currentDiagram, this.model));
    } else {
        alert("You need a diagram opened before placing elements.");
    }
};
Project.prototype.evCreateNewRealization = function() {
    if (this.currentDiagram !== null) {
        var newRelationship = new Inheritance("Unnamed realization", false, true);
        var newStereo = new Stereotype("realize");
        newRelationship.addStereotype(newStereo);

        this.currentDiagram.setLeftMouseClickCallback(null);
        this.currentDiagram.setLeftMouseDragCallback(new CreateRelationshipCallback(newRelationship, this.currentDiagram, this.model));
    } else {
        alert("You need a diagram opened before placing elements.");
    }
};
Project.prototype.evNewProject = function() {

    this.newProject();
    this.createNewDiagram();
};
Project.prototype.evCanvasChange = function(event) {

    this.resizeDiagramCanvas();

    if (this.currentDiagram !== null) {
        this.currentDiagram.draw();
    } else {
    }
};
Project.prototype.evAddModelElementToCurrentDiagram = function(elementUid) {

    if (this.currentDiagram !== null) {

        // check if diagram element already present
        var gr = this.currentDiagram.getElementByModelId(elementUid);
        if (gr !== null) {
            this.currentDiagram.setSelectedElement(gr);
            // center view to element center
            this.currentDiagram.setViewCenter(gr.getCenterPosition());
            return;
        }

        // get model element for id
        var element = this.model.getElementById(elementUid);
        if (element === null) {
            return;
        }

        // create graphics for model element
        var result = this.currentDiagram.createGraphicsForModelElement(element);

        if (result.error === null) {
            // add element to diagram
            var graphics = result.result;
            this.currentDiagram.addElement(graphics);
            this.currentDiagram.setSelectedElement(graphics);

        } else {
            alert("Unable to add element to diagram: " + result.error);
        }
    } else {
        alert("You need a diagram opened before placing elements.");
    }
};
